package com.sifou.courses.service.impl;

import com.sifou.courses.Constant;
import com.sifou.courses.dto.SeckillGoodsDto;
import com.sifou.courses.dto.SeckillOrderDto;
import com.sifou.courses.exception.CustomException;
import com.sifou.courses.repository.entity.TSeckillGoods;
import com.sifou.courses.repository.mapper.TSeckillGoodsMapper;
import com.sifou.courses.service.ISeckillService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.retry.RetryException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;

/**
 * @author liuzhongxu
 * @date 2020/8/19
 */
@Slf4j
@Service("seckillRedisService")
public class SeckillRedisServiceImpl implements ISeckillService {

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private TSeckillGoodsMapper goodsMapper;

    @Override
    public SeckillGoodsDto checkStock(Integer id) {
        String[] keys = {Constant.PREFIX_COUNT + id, Constant.PREFIX_SALE + id};
        Map<String, Integer> buckets = redissonClient.getBuckets().get(keys);
        // 库存
        Integer count = buckets.get(Constant.PREFIX_COUNT + id);
        // 已售
        Integer sale = buckets.get(Constant.PREFIX_SALE + id);
        if (Objects.nonNull(count) && count > 0) {
            log.debug("库存充足：count={}", count);
            SeckillGoodsDto goodsDto = new SeckillGoodsDto();
            goodsDto.setId(id);
            goodsDto.setCount(count);
            goodsDto.setSale(sale);
            return goodsDto;
        }
        log.debug("库存不足：count={}", count);
        throw new CustomException("库存不足");
    }

    @Override
    public Integer saleStock(SeckillGoodsDto goodsDto) {
        RAtomicLong atomicLong = redissonClient.getAtomicLong(Constant.PREFIX_COUNT + goodsDto.getId());
        long stock = atomicLong.addAndGet(-1);
        if (stock < 0) {
            // 回滚库存
            atomicLong.addAndGet(1);
            log.debug("回滚库存成功：count={} 已售：sale={}", stock, goodsDto.getSale());
            return -1;
        } else {
            // TODO 异步扣库存
            TSeckillGoods tSeckillGoods = new TSeckillGoods();
            BeanUtils.copyProperties(goodsDto, tSeckillGoods);
            goodsMapper.updateStock(tSeckillGoods);
            log.debug("库存：count={} 已售：sale={}", stock, goodsDto.getSale());
        }
        return 1;
    }

    @Override
    public Integer rollBackStock(SeckillOrderDto orderDto) {
        // 回滚库存
        TSeckillGoods tSeckillGoods = goodsMapper.selectById(orderDto.getGoodsId());
        int opCount = goodsMapper.rollBackStock(tSeckillGoods);
        if (opCount > 0) {
            log.debug("回滚库存成功：count={} 已售：sale={} 订单：{}", tSeckillGoods.getCount(), tSeckillGoods.getSale(), orderDto);
            RAtomicLong atomicLong = redissonClient.getAtomicLong(Constant.PREFIX_COUNT + orderDto.getGoodsId());
            atomicLong.addAndGet(1);
            return opCount;
        }
        log.debug("回滚库存失败：count={} 已售：sale={} 订单：{}", tSeckillGoods.getCount(), tSeckillGoods.getSale(), orderDto);
        throw new RetryException("回滚库存失败");
    }
}
